#include <cmd/common.h>
#include "discovery.h"

#include <boost/asio.hpp>
#include <boost/asio/detail/socket_option.hpp>
#include <boost/system/system_error.hpp>
#include <boost/algorithm/string.hpp>
#include <boost/array.hpp>

#include <boost/asio/thread_pool.hpp>
#include <boost/endian/conversion.hpp>

Discovery::Discovery() {
    std::thread([&]{
        boost::asio::io_service io;
        boost::system::error_code ec;
        boost::asio::ip::udp::socket socket(io,boost::asio::ip::udp::endpoint(boost::asio::ip::udp::v4(), 9001));
        socket.set_option(boost::asio::ip::multicast::hops(10),ec);
        socket.set_option(boost::asio::socket_base::keep_alive(true),ec);
        boost::asio::ip::udp::endpoint multicast_endpoint(boost::asio::ip::address::from_string("224.0.0.1"), 9001);
        socket.set_option(boost::asio::ip::multicast::join_group(multicast_endpoint.address()),ec);
        if (ec) {
            BOOST_LOG_TRIVIAL(error)<< "Failed to join multicast group";
            return;
        }
        while (!_stop) {
            std::string sendgram(MESSAGE);
            sendgram+=":";
            sendgram+=settings.get<std::string>("http.port", "80");
            sendgram+="_";
            sendgram+=settings.get<std::string>("device/type", "1");
            socket.send_to(boost::asio::buffer(sendgram),multicast_endpoint);
            std::this_thread::sleep_for(std::chrono::seconds(3));

            long long recv_len;
            do{
                std::vector<char> datagram(1024,0);
                boost::asio::ip::udp::endpoint sender_endpoint;
                recv_len = socket.receive_from(boost::asio::buffer(datagram), sender_endpoint);
                if(recv_len && boost::algorithm::starts_with(&datagram[0],MESSAGE)){
                    std::string ip = sender_endpoint.address().to_string()+std::string(&datagram[0]).substr(strlen(MESSAGE));
                    std::unique_lock<std::shared_mutex> lock(_readWriteLock);
                    if(_ips.find(ip)==_ips.end()){
                        BOOST_LOG_TRIVIAL(info)<<"add device:"<<ip;
                    }
                    _ips[ip]=10;
                }
            }while(recv_len>0);
            {
                std::unique_lock<std::shared_mutex> lock(_readWriteLock);
                for(auto it=_ips.begin();it!=_ips.end();){
                    (*it).second--;
                    if ((*it).second==0){
                        BOOST_LOG_TRIVIAL(info)<<"delete device:"<<(*it).first;
                        it = _ips.erase(it);
                    }
                    else
                        it++;
                }
            }
        }
        socket.set_option(boost::asio::ip::multicast::leave_group(multicast_endpoint.address()),ec);
        socket.close();
    }).detach();
}